package club.kingyin.easycache.proxy;

import club.kingyin.easycache.cache.ProxyMethodCache;
import club.kingyin.easycache.component.annotation.*;
import club.kingyin.easycache.component.annotation.Module;
import club.kingyin.easycache.method.CacheMethod;
import club.kingyin.easycache.proxy.adapter.AbstractEasyCacheMethod;
import club.kingyin.easycache.proxy.adapter.CacheMethodKeyAdapter;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.lang.reflect.Method;

// 4个增强处理器 生成key前后，调用
public class ProxyEasyCache extends AbstractEasyCacheMethod<MethodInvocation> implements MethodInterceptor {

    public ProxyEasyCache(ProxyMethodCache cache, CacheMethodKeyAdapter adapter) {
        super(cache, adapter);
    }

    @Override
    protected CacheMethod adaptation(MethodInvocation target) {
        return new CacheMethod() {
            @Override
            public Method getMethod() {
                return target.getMethod();
            }

            @Override
            public Object proceed() throws Throwable {
                return target.proceed();
            }

            @Override
            public Object[] getArguments() {
                return target.getArguments();
            }
        };
    }

    @Nullable
    @Override
    public Object invoke(@Nonnull MethodInvocation invocation) {
        return odInvoke(invocation);
    }

    @Override
    public Object invoke(CacheMethod target) {
        target.getMethod().setAccessible(true);
        shouldProxy(target.getMethod());
        try {
            // 如果返回值为void则不执行缓存代理
            if (target.getMethod().getReturnType().equals(Void.TYPE))
                return target.proceed();
            // 对使用了相关注解的方法进行缓存代理
            return shouldProxy(target.getMethod()) || shouldProxy(target.getArguments())?super.invoke(target):target.proceed();
        } catch (Throwable throwable) {
            throw new club.kingyin.easycache.exception.InvokeException(throwable);
        }
    }

    private boolean shouldProxy(Method method) {
        return method.isAnnotationPresent(EasyCache.class) ||
                method.isAnnotationPresent(Module.class) ||
                method.isAnnotationPresent(MethodName.class) ||
                method.isAnnotationPresent(Remove.class) ||
                method.isAnnotationPresent(Removes.class) ||
                method.isAnnotationPresent(Expire.class) ||
                method.isAnnotationPresent(InvokeException.class) ||
                method.isAnnotationPresent(InvokeHandler.class) ||
                method.isAnnotationPresent(CacheHandler.class) ||
                method.isAnnotationPresent(Ignore.class) ||
                method.isAnnotationPresent(Param.class);
    }

    private boolean shouldProxy(Object[] args) {
        for (Object arg : args) {
            return arg.getClass().isAnnotationPresent(EasyCache.class) ||
                    arg.getClass().isAnnotationPresent(Module.class) ||
                    arg.getClass().isAnnotationPresent(MethodName.class) ||
                    arg.getClass().isAnnotationPresent(Remove.class) ||
                    arg.getClass().isAnnotationPresent(Removes.class) ||
                    arg.getClass().isAnnotationPresent(Expire.class) ||
                    arg.getClass().isAnnotationPresent(InvokeException.class) ||
                    arg.getClass().isAnnotationPresent(InvokeHandler.class) ||
                    arg.getClass().isAnnotationPresent(CacheHandler.class) ||
                    arg.getClass().isAnnotationPresent(Ignore.class) ||
                    arg.getClass().isAnnotationPresent(Param.class);
        }
        return false;
    }
}
